package com.sprucetec.live.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.sprucetec.live.base.entity.User;
import com.sprucetec.live.common.util.HttpClient;
import com.sprucetec.live.common.util.OssUtil;
import com.sprucetec.live.common.webcontext.LoginUserContext;
import com.sprucetec.live.constants.SprucetecConstant;
import com.sprucetec.live.enums.LiveLoginTypeEnum;
import com.sprucetec.live.service.RedisService;
import com.sprucetec.live.util.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 登录拦截器
 * @time: 2018年7月9日 上午9:13:28
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Value(value = "${sso.full.loginurl.live}")
    String ssoFullLoginurl;

    @Value(value = "${sso.full.loginurl.liveview}")
    String ssoFullLoginurlView;

    @Value(value = "${sso.full.loginurl.liveviewperson}")
    String ssoFullLoginurlViewPerson;

    @Value(value = "${sso.user.cookei.envdomain}")
    private String domain;

    @Value(value = "${sso.loginurl}")
    String ssoLoginurl;

    @Value(value = "${auth.serverurl}")
    String authServerUrl;

    @Value(value = "${sso.user.cookie.name}")
    String userCookieName;

    @Value(value = "${system.key}")
    String systemKey;

    @Value("${sso.redirect.url.pc}")
    String ssoRedirectUrlPC;

    @Value("${sso.redirect.url.app}")
    String ssoRedirectUrlAPP;

    @Value("${oas.context.address.url}")
    String OAS_CONTEXT_ADDRESS_URL;

    private static final String flag = "flag";

    @Autowired
    private RedisService redisService;

    private final static String HTTP_HEAD_REQUEST_TYPE_KEY = "X-Requested-With";

    private final static String HTTP_HEAD_AJAX_VALUE = "XMLHttpRequest";

    private final static String LOGIN_USER_TOKEN = "LIVE:LOGIN_USER_CACHE:token={0}";


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {

        boolean validateRedirect = false;
        String loginUrl = ssoFullLoginurl;
        String uri = request.getRequestURI();
        StringBuffer checkUrl = request.getRequestURL();
        log.info("登录拦截器： url =>", checkUrl.toString());
        Map map = checkLiveView(request, loginUrl, validateRedirect);// PC用户端系列校验
        loginUrl = (String) map.get("loginUrl");
        validateRedirect = (boolean) map.get("validateRedirect");
        uri = uri.substring(request.getContextPath().length());
        try {
            // 从cookie中获取token值
            String token = null;
            Cookie[] cookies = request.getCookies();
            if (cookies == null) {
                if (isAjax(request)) {
                    requestAjax(validateRedirect, response, loginUrl);
                } else {
                    response.sendRedirect(loginUrl);
                }
                return false;
            }

            for (Cookie cookie : cookies) {
                if (userCookieName.equals(cookie.getName())) {
                    token = cookie.getValue();
                    break;
                }
            }
            //token为空，未登录
            if (token == null || token.length() < 1) {
                if (isAjax(request)) {
                    requestAjax(validateRedirect, response, loginUrl);
                } else {
                    response.sendRedirect(loginUrl);
                }
                return false;
            }
            //获取用户真实ip
            String realIp = OssUtil.getRealIp(request);
            if (uri.contains("getPrivateIp")) {
                request.getSession().invalidate();
                this.returnJson(response, SprucetecConstant.OpCode.SSO_NOT_LOGIN, realIp, false);
                return true;

            }
            if (StringUtils.isBlank(realIp)) {
                log.error("获取用户真实ip失败");
                return false;
            }
            log.info("ip:" + realIp);
            //封装登录用户信息
            User loginUser = getLoginUser(token, uri, realIp, request);
            if (loginUser == null) {
                if (isAjax(request)) {
                    requestAjax(validateRedirect, response, loginUrl);
                } else {
                    response.sendRedirect(loginUrl);
                }
                return false;
            }

            /**
             * 因现在前端控制菜单的展示 所以不需要这块的权限校验
             if (uri.equals("/") || uri.contains("common/") || uri.contains("category/") || uri.contains("product/") || uri.contains("purchaseLog/")
             || uri.contains("purchasePrice/") || uri.contains("user/") || uri.contains("purchaseApply/") || uri.contains("task/") || uri.contains("project/")
             || uri.contains("purchaseOrder/")  || uri.contains("supplier/")  || uri.contains("menu/")  || uri.contains("purchaseReceive/")  || uri.contains("purchaseSku/")
             || uri.contains("purchaseStrategy/") || uri.contains("approval/")) {
             return true;
             }
             if (loginUser.getAccessList() == null) {
             this.returnJson(response, SprucetecConstant.OpCode.SSO_NO_PERMISSION, "此功能无权限访问！", false);
             return false;
             }
             if (!loginUser.getAccessList().contains(uri)) {
             this.returnJson(response, SprucetecConstant.OpCode.SSO_NO_PERMISSION, "此功能无权限访问！", false);
             return false;
             }
             */
            return true;

        } catch (Exception e) {
            log.error("LoginInterceptor catch到异常", e);
            //发生异常,返回异常信息
            request.getSession().invalidate();
            this.returnJson(response, SprucetecConstant.OpCode.SSO_NOT_LOGIN, "用户登录信息失效！", false);
            return false;
        }
    }

    /**
     * PC用户端系列校验
     *
     * @param request
     * @param loginUrl
     * @param validateRedirect
     */
    private Map checkLiveView(HttpServletRequest request, String loginUrl, boolean validateRedirect) {
        Map map = new HashMap();
        // 判断是手机端还是PC端,如果是手机端，并判断是钉钉应用内部的，才进行免登的跳转
        String userAgent = request.getHeader("User-Agent");
        if (userAgent != null && !"".equals(userAgent) && HttpClientUtil.isMobileDevice(userAgent)) {
            ssoFullLoginurlView = ssoRedirectUrlAPP;
        } else {
            ssoFullLoginurlView = ssoRedirectUrlPC;
        }
        if (request.getParameter(flag) != null && LiveLoginTypeEnum.LIVEVIEW.getType().equals(request.getParameter(flag))) { // PC端
            loginUrl = ssoFullLoginurlView;
        } else if (request.getParameter(flag) != null && LiveLoginTypeEnum.DETAIL.getType().equals(request.getParameter(flag))) { // 直播详情
            loginUrl = ssoFullLoginurlView + getSuffixRedirectUrl(request.getParameter("liveNo"), LiveLoginTypeEnum.DETAIL.getType());
            validateRedirect = true;
        } else if (request.getParameter(flag) != null && LiveLoginTypeEnum.PERSON.getType().equals(request.getParameter(flag))) { // 私人聊天界面
            loginUrl = ssoFullLoginurlView + getSuffixRedirectUrl(request.getParameter("liveNo"), LiveLoginTypeEnum.PERSON.getType());
            validateRedirect = true;
        } else if (request.getParameter(flag) != null && LiveLoginTypeEnum.BACKVIDEODETAIL.getType().equals(request.getParameter(flag))) {
            loginUrl = ssoFullLoginurlView + getSuffixRedirectUrl(request.getParameter("liveNo"), LiveLoginTypeEnum.BACKVIDEODETAIL.getType());
            validateRedirect = true;
        }
        map.put("loginUrl", loginUrl);
        map.put("validateRedirect", validateRedirect);
        return map;
    }

    /**
     * 获取地址后缀
     *
     * @param liveNo
     * @param type
     * @return
     */
    private String getSuffixRedirectUrl(String liveNo, String type) {
        ///URLEncoder.encode(OAS_CONTEXT_ADDRESS_URL + request.getRequestURI() + "?" + request.getQueryString())
        return URLEncoder.encode(OAS_CONTEXT_ADDRESS_URL + "/liveview/#/" + type + "?liveNo=" + liveNo);
    }


    private void requestAjax(boolean validateRedirect, HttpServletResponse response, String loginUrl) throws Exception {
        if (validateRedirect) {
            this.returnJson(response, SprucetecConstant.OpCode.PLATFORM_SSO_NOT_LOGIN, loginUrl, false);
        } else {
            this.returnJson(response, SprucetecConstant.OpCode.SSO_NOT_LOGIN, "获取cookie失败！", false);
        }
    }

    private User getLoginUser(String token, String uri, String ip, HttpServletRequest request) {
        //先从redis中获取登录用户 如果没有 则封装后set进redis
        User loginUser = (User) redisService.get(MessageFormat.format(LOGIN_USER_TOKEN, token));
        if (loginUser != null) {
            //set user对象到request中 因为aop模块要用
            request.setAttribute(User.USER_KEY, loginUser);
            LoginUserContext.setUser(loginUser);
            return loginUser;
        }
        log.info("getLoginUser token = {}, uri = {}", token, uri);
        String result = checkUser(token, uri, ip);
        log.info("getLoginUser result = {}", result);
        if (StringUtils.isBlank(result)) {
            return null;
        }
        JSONObject jsonResult = JSONObject.parseObject(result);
        JSONObject jsonData = jsonResult.getJSONObject("data");
        if (jsonData == null) {
            return null;
        }
        String authUserId = jsonData.getString("id");
        String uid = jsonData.getJSONObject("cas_info").getString("uid");
        if (StringUtils.isBlank(uid)) { // 为了兼容没有uid的用户，使用手机号当做uid，为了支持2020.06.06周年庆直播活动
            uid = jsonData.getJSONObject("cas_info").getString("telephonenumber");
        }
        String userName = jsonData.getJSONObject("cas_info").getString("givenname");
        String email = jsonData.getJSONObject("cas_info").getString("mail");
        String phone = jsonData.getJSONObject("cas_info").getString("telephonenumber");

        ArrayList<String> roleList = Lists.newArrayList();
        JSONObject role = jsonData.getJSONObject("roles");
        if (role != null) {
            JSONObject liveObj = role.getJSONObject(systemKey);
            if (null != liveObj) {
                String name = liveObj.getString("name");
                if (StringUtils.isNotBlank(name)) {
                    String[] split = name.split(",");
                    roleList.addAll(Lists.newArrayList(split));
                }
            }
        }

        loginUser = new User();
        loginUser.setToken(token);
        loginUser.setUid(uid);
        loginUser.setName(userName);
        loginUser.setEmail(email);
        loginUser.setIp(ip);
        loginUser.setEmailPrefix(StringUtils.substringBefore(email, "@"));
        loginUser.setPhone(phone);
        loginUser.setId(Long.parseLong(authUserId));
        Set<String> allUrl = getAllAccess(token, ip);
        loginUser.setAccessList(allUrl);
        loginUser.setRoleList(roleList);

        if (!CollectionUtils.isEmpty(loginUser.getRoleList())) {
            loginUser.setRoles(String.join(",", roleList));
        } else {
            loginUser.setRoles("");
        }
        redisService.set(MessageFormat.format(LOGIN_USER_TOKEN, token), loginUser, TimeUnit.MINUTES, 5);
        //set user对象到request中 因为aop模块要用
        request.setAttribute(User.USER_KEY, loginUser);
        LoginUserContext.setUser(loginUser);
        return loginUser;
    }

    private String checkUser(String token, String url, String ip) {
        Map<String, String> param = new HashMap<>();
        param.put("user_token", token);
        param.put("entry", url);
        param.put("real_ip", ip);
        param.put("system_key", systemKey);

        /*SimpleRetryPolicy policy = new SimpleRetryPolicy();
        //重试次数
        policy.setMaxAttempts(1);
        RetryTemplate template = new RetryTemplate();
        template.setRetryPolicy(policy);
        try {
            return template.execute((RetryCallback<String, Throwable>) context -> HttpClient.post(authServerUrl + "/user/tokeninfo", param));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            log.error("调用auth http接口check token 失败", throwable.getMessage());
        }*/

        return HttpClient.post(authServerUrl + "/user/tokeninfo", param);
    }

    /**
     * 是否ajax请求
     *
     * @param request
     * @return
     */
    public static boolean isAjax(HttpServletRequest request) {
        String requestType = request.getHeader(HTTP_HEAD_REQUEST_TYPE_KEY);
        return HTTP_HEAD_AJAX_VALUE.equals(requestType);
    }

    /**
     * 向前端返回json数据
     */
    private void returnJson(HttpServletResponse response, int rspCode, String rspMsg, boolean success) throws Exception {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        try {
            JSONObject resultJson = new JSONObject();
            resultJson.put("rspCode", rspCode);
            resultJson.put("rspMsg", rspMsg);
            resultJson.put("success", success);

            writer = response.getWriter();
            writer.print(resultJson.toJSONString());

        } catch (IOException e) {
            log.error("response error", e);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    /**
     * 获取用户拥有系统的所有的权限
     *
     * @param token
     * @param ip
     * @return
     */
    private Set<String> getAllAccess(String token, String ip) {
        Set<String> allUrl = new HashSet<>();
        Map<String, String> param = new HashMap<>();
        param.put("user_token", token);
        param.put("system_key", systemKey);
        param.put("real_ip", ip);
        StringBuilder urlBuffer = new StringBuilder();
        urlBuffer.append(authServerUrl).append("/role/listaccessbytokenandkey");
        String str = HttpClient.post(urlBuffer.toString(), param);

        Map<String, Object> allAccessMap = JSONObject.parseObject(str);

        if (!allAccessMap.get("ret").equals(1)) {
            return null;
        }
        List<Map<String, Object>> accesses = (List<Map<String, Object>>) allAccessMap.get("data");
        if (CollectionUtils.isEmpty(accesses)) {
            return null;
        }
        for (Map<String, Object> tem : accesses) {
            allUrl.add((String) tem.get("entry"));
        }
        return allUrl;
    }

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {

    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {

    }
}
